﻿using System.Diagnostics.CodeAnalysis;

namespace zijian666.AnyExtensions;

/// <summary>
/// 分页相关扩展方法
/// </summary>
public static class PagingExtensions
{
    /// <summary>
    /// Linq分页
    /// </summary>
    /// <param name="pageIndex">分页页数索引,从0开始</param>
    /// <param name="pageSize">分页每页数量</param>
    /// <param name="returnTotal">是否返回总条数, 默认true</param>
    /// <returns></returns>
    public static PagedList<TSource> Paging<TSource>(this IQueryable<TSource> queryable, int pageIndex, int pageSize, bool returnTotal = true)
        => Paging(queryable, Pager.FromPage(pageIndex, pageSize), returnTotal);

    /// <summary>
    /// Linq分页
    /// </summary>
    /// <param name="pager">分页参数</param>
    /// <param name="returnTotal">是否返回总条数, 默认true</param>
    /// <returns></returns>
    public static PagedList<TSource> Paging<TSource>(this IQueryable<TSource> queryable, Pager pager, bool returnTotal = true)
    {
        if (queryable is null)
        {
            return new PagedList<TSource>(0, pager);
        }
        var countable = queryable;
        if (!pager.OrderBy.IsNullOrWhiteSpace())
        {
            queryable = queryable.OrderBy(pager.OrderBy);
        }
        if (pager.BeginRowIndex > 0)
        {
            queryable = queryable.Skip((int)pager.BeginRowIndex);
        }
        if (pager.PageSize is > 0 and < int.MaxValue)
        {
            queryable = queryable.Take(pager.PageSize);
        }
        else
        {
            returnTotal = false;
        }

        var items = queryable.ToList();
        var total = 0L;
        if (items.Count < pager.PageSize)
        {
            total = pager.BeginRowIndex == 0 ? items.Count : pager.BeginRowIndex + items.Count;
        }
        else if (returnTotal)
        {
            total = countable.Count();
        }
        var paged = new PagedList<TSource>(total, pager);
        paged.AddRange(items);
        return paged;
    }

    /// <summary>
    /// 将Linq分页列表内的元素转换为另一种类型
    /// </summary>
#if NET6_0_OR_GREATER
    [return: NotNullIfNotNull("source")]
#endif
    public static PagedList<TResult>? Select<TSource, TResult>(this PagedList<TSource>? source, Func<TSource, TResult> selector)
    {
        if (source is null)
        {
            return null;
        }
        var newlist = new PagedList<TResult>(source.TotalItemCount, source.Pager);
        newlist.AddRange(Enumerable.Select(source, selector));
        return newlist;
    }
    /// <summary>
    /// 将一个 <see cref="IEnumerable{T}"/> 转换为分页列表。
    /// </summary>
    /// <typeparam name="TResult">源集合中元素的类型。</typeparam>
    /// <param name="source">要转换的源集合。</param>
    /// <param name="total">数据总记录数。</param>
    /// <param name="pager">分页信息对象。</param>
    /// <returns>包含分页信息的结果列表。</returns>
    public static PagedList<TResult> ToPagedList<TResult>(this IEnumerable<TResult> source, long total, Pager pager)
    {
        var paged = new PagedList<TResult>(total, pager);
        paged.AddRange(source);
        return paged;
    }
}

/// <summary>
/// 分页参数
/// </summary>
public struct Pager
{
    /// <summary>
    /// 不分页
    /// </summary>
    public static Pager NonPaging { get; } = new(0, int.MaxValue, 0) { NoReturnTotalCount = true };
    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="pageIndex">分页页数索引,从0开始</param>
    /// <param name="pageSize">分页每页数量</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="pageIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="pageSize"/>小于等于 0。</exception>
    public static Pager FromPage(long pageIndex, int pageSize, string? orderby = null)
    {
        if (pageIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(pageIndex));
        }
        if (pageSize <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(pageSize));
        }
        return new(pageIndex, pageSize, pageSize * pageIndex) { OrderBy = orderby };
    }
    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="beginIndex">开始数据行号，从0开始</param>
    /// <param name="count">返回数据条数</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="beginIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="count"/>小于等于 0。</exception>
    public static Pager FromLimit(long beginIndex, int count, string? orderby = null)
    {
        if (beginIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(beginIndex));
        }
        if (count <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(count));
        }
        return new(-1, count, beginIndex) { OrderBy = orderby };
    }
    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="beginIndex">开始数据行号，从0开始</param>
    /// <param name="endIndex">结束数据行号</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="beginIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="endIndex"/>小于等于 <paramref name="beginIndex"/>。</exception>
    public static Pager FromBetween(long beginIndex, long endIndex, string? orderby = null)
    {
        if (beginIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(beginIndex));
        }
        if (endIndex <= beginIndex)
        {
            throw new ArgumentOutOfRangeException(nameof(endIndex));
        }
        return new(-1, (int)(endIndex - beginIndex + 1), beginIndex) { OrderBy = orderby };
    }

    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="pageIndex">分页页数索引,从0开始</param>
    /// <param name="pageSize">分页每页数量</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="pageIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="pageSize"/>小于等于 0。</exception>
    public static Pager FromPage(int pageIndex, int pageSize, string? orderby = null)
        => FromPage((long)pageIndex, pageSize, orderby);
    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="beginIndex">开始数据行号，从0开始</param>
    /// <param name="count">返回数据条数</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="beginIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="count"/>小于等于 0。</exception>
    public static Pager FromLimit(int beginIndex, int count, string? orderby = null)
        => FromLimit((long)beginIndex, count, orderby);
    /// <summary>
    /// 创建分页参数
    /// </summary>
    /// <param name="beginIndex">开始数据行号，从0开始</param>
    /// <param name="endIndex">结束数据行号</param>
    /// <param name="orderby">排序参数</param>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="beginIndex"/>小于 0。</exception>
    /// <exception cref="ArgumentOutOfRangeException"> <paramref name="endIndex"/>小于等于 <paramref name="beginIndex"/>。</exception>
    public static Pager FromBetween(int beginIndex, int endIndex, string? orderby = null)
        => FromBetween((long)beginIndex, (long)endIndex, orderby);

    private Pager(long pageIndex, int pageSize, long beginRowIndex) : this()
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        BeginRowIndex = beginRowIndex;
    }
    /// <summary>
    /// 分页页数,从0开始
    /// </summary>
    public long PageIndex { get; private set; }
    /// <summary>
    /// 分页每页数量
    /// </summary>
    public int PageSize { get; }
    /// <summary>
    /// 开始行号 从0开始
    /// </summary>
    public long BeginRowIndex { get; }
    /// <summary>
    /// 结束行号
    /// </summary>
    public readonly long EndRowIndex => BeginRowIndex + PageSize - 1;
    /// <summary>
    /// 排序参数
    /// </summary>
    public string? OrderBy { get; set; }

    /// <summary>
    /// 是否不返回总条数
    /// </summary>
    public bool NoReturnTotalCount { get; set; } = false;
}

/// <summary>
/// 分页集合
/// </summary>
/// <typeparam name="T"></typeparam>
public class PagedList<T>(long _total, Pager pager) : List<T>
{
    /// <summary>
    /// 总条数
    /// </summary>
    public long TotalItemCount => Math.Max(_total, Count);
    /// <summary>
    /// 总页数
    /// </summary>
    public long TotalPageCount => (TotalItemCount + Pager.PageSize - 1) / Math.Max(1, Pager.PageSize);
    /// <summary>
    /// 分页参数
    /// </summary>
    public Pager Pager { get; } = pager;
    /// <summary>
    /// 将指定集合的元素添加到 <seealso cref="List{T}"/> 的末尾。
    /// </summary>
    public new PagedList<T> AddRange(IEnumerable<T> collection)
    {
        if (collection is not null)
        {
            base.AddRange(collection);
        }
        return this;
    }
}
